import {
  Scene,//场景对象
  PerspectiveCamera,//透视投影相机
  AxesHelper,//辅助线
  WebGLRenderer,//渲染器
  Camera,//相机类
  SRGBColorSpace,//色彩空间
  AmbientLight,//环境光
  Raycaster,//射线拾取模型 鼠标点击选中模型
  Vector2//二维向量对象OrbitControls
} from 'three'
import * as THREE from 'three';
import mitt, { type Emitter } from 'mitt';
import Events from './Events';
import { throttle } from 'lodash';
//相机轨道控制器 three扩展包，官方文件包引入
//three/addons/路径之后对应的是three.js官方文件包`/examples/jsm/`中的js库
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
//性能监视器扩展包
import Stats from 'three/examples/jsm/libs/stats.module';
// 引入天空盒
import SkyBoxs from '../SkyBoxs';
export type Animate = {
  fun: (arg: any) => any,
  content: any,
}
export default class Viewer{
  public viewerDom!: HTMLElement;
  public id: string;
  public scene!: Scene;//!:ts类型表示强制有值
  public camera!: PerspectiveCamera;
  public controls!: OrbitControls;
  public renderer!: WebGLRenderer;
  public raycaster!: Raycaster;
  public skyboxs!: SkyBoxs;
  public emitter!: Emitter<any>;
  public mouse!: Vector2;
  public animateEventList: any[] = [];
  public statsControls!: Stats;
  public mouseEvent!: MouseEvent | undefined;
  public isDestroy: boolean = false;
  
  // threejs中的基类，是一切的三维空间的基础，就是一个抽象概念，表示着一个可以在三维空间中自由移动、自由旋转、自由缩放，可以控制显示隐藏的物体
  public raycasterObjects: THREE.Object3D[] = [];
  constructor(id: string) {
    this.id = id;
    this.initViewer();
  }
  public addAnimate(animate: Animate) {
    this.animateEventList.push(animate);
  }
  private statsUpdate(statsControls: any) {
    //requestAnimationFrame循环调用的函数中调用方法update(),来刷新时间
    statsControls.update();
  }

  private addStats() {
    if (!this.statsControls) this.statsControls = new Stats();
    this.statsControls.dom.style.position = 'absolute';
    this.viewerDom.appendChild(this.statsControls.dom);
    // 添加到动画
    this.addAnimate({
      fun: this.statsUpdate,
      content: this.statsControls,
    });
  }

  private initViewer() {
    this.emitter = mitt();

    this.initRenderer();
    this.initScene();
    this.initLight();
    this.initCamera();
    this.initControl();
    // this.initSkybox();

    this.raycaster = new Raycaster();
    this.mouse = new Vector2();

    const animate = () => {
      if (this.isDestroy) return;
      requestAnimationFrame(animate);

      this.updateDom();
      this.readerDom();
      // 全局的公共动画函数，添加函数可同步执行
      this.animateEventList.forEach(event => {
        // event.fun && event.content && event.fun(event.content);
        if (event.fun && event.content) {
          event.fun(event.content);
        }
      });
    };

    animate();
  }
  private initScene() {
  
    this.scene = new Scene();
  }
  private initCamera() {
    // 30:视场角度, window.innerWidth / window.innerHeight:Canvas画布宽高比, 1:近裁截面, 3000：远裁截面
    //渲染相机
    this.camera = new PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 3000)
      //设置相机位置
    this.camera.position.set(4, 2, -3);
    //设置相机方向
    this.camera.lookAt(200, 200, 200);
  }
  private initControl() {
  
    this.controls = new OrbitControls(
      this.camera as Camera,
      this.renderer?.domElement
    )
     // 使动画循环使用时阻尼或自转 意思是否有惯性
    this.controls.enableDamping = false;
    // 定义平移时如何平移相机的位置 控制不上下移动
    this.controls.screenSpacePanning = false;
     //设置相机距离原点的最近距离 
    this.controls.minDistance = 2;
     //设置相机距离原点的最远距离 
    this.controls.maxDistance = 1000;
    //相机控件监听重新渲染
    this.controls.addEventListener('change', ()=>{
      this.renderer.render(this.scene, this.camera);
      console.log('camera.position',this.camera.position);
    });
      //controls.enablePan = false; //禁止右键拖拽
      // controls.enableZoom = false;//禁止缩放
      // controls.enableRotate = false; //禁止旋转
      // controls.minZoom = 0.5; // 最小缩放比例
      // controls.maxZoom = 2; // 放大最大比例
      // // 上下旋转范围
      // controls.minPolarAngle = 0;
      // controls.maxPolarAngle = Math.PI;
      // // 左右旋转范围
      // controls.minAzimuthAngle = -Math.PI * (100 / 180);
      // controls.maxAzimuthAngle = Math.PI * (100 / 180);
  }

  private initLight() {
  //光颜色0xffffff, 光密度0.6
    //初始化方向光
    const ambient = new AmbientLight(0xffffff, 0.6)
    this.scene.add(ambient);
    //  平行光
    const light = new THREE.DirectionalLight( 0xffffff );
    light.position.set( 0, 200, 100 );
    light.castShadow = true;

    light.shadow.camera.top = 180;
    light.shadow.camera.bottom = -100;
    light.shadow.camera.left = -120;
    light.shadow.camera.right = 400;
    light.shadow.camera.near = 0.1;
    light.shadow.camera.far = 400;
    // 设置mapSize属性可以使阴影更清晰，不那么模糊
    light.shadow.mapSize.set(1024, 1024);

    this.scene.add(light);
  }

  private initSkybox() {
    if (!this.skyboxs) this.skyboxs = new SkyBoxs(this);
    this.skyboxs.addSkybox('night');
    this.skyboxs.addFog();
  }
  public initRaycaster() {
    //标注类型，eventName: keyof HTMLElementEventMap事件类型规范
    const initRaycasterEvent: Function = (eventName: keyof HTMLElementEventMap): void => {
      const funWrap = throttle(
        (event: any) => {
          this.mouseEvent = event;
      //画布全屏计算方法
      this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
          this.mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
          let dom = document.getElementById('three')
          let styleObjLeft = dom.getBoundingClientRect().left
          let styleObjTop =dom.getBoundingClientRect().top
          // console.log(styleObjLeft,"=======>>>121212");
          
          //画布非全屏计算方法
          this.mouse.x = ((event.clientX - styleObjLeft) / dom.clientWidth) * 2 - 1;
          // dom.offsetLeft -- dom元素距离浏览器左侧的距离   dom.clientWidth -- dom元素宽度
          this.mouse.y = -((event.clientY - styleObjTop) / dom.clientHeight) * 2 + 1;
          // dom.offsetTop -- dom元素距离浏览器顶部的距离    dom.clientHeight -- dom元素高度
      // @ts-expect-error
      this.emitter.emit(Events[eventName].raycaster, this.getRaycasterIntersectObjects());
    },
        50)
        this.viewerDom.addEventListener(eventName, funWrap, false);
    }
    initRaycasterEvent('click');
    initRaycasterEvent('dblclick');
    initRaycasterEvent('mousemove');
  }
    // 渲染dom
  private readerDom() {
       //执行渲染操作
      this.renderer?.render(this.scene as Scene, this.camera as Camera);
    }
  private updateDom() {
    this.controls.update();
    // 更新参数
    this.camera.aspect = this.viewerDom.clientWidth / this.viewerDom.clientHeight; // 摄像机视锥体的长宽比，通常是使用画布的宽/画布的高
    this.camera.updateProjectionMatrix(); // 更新摄像机投影矩阵。在任何参数被改变以后必须被调用,来使得这些改变生效
    this.renderer.setSize(this.viewerDom.clientWidth, this.viewerDom.clientHeight);
    this.renderer.setPixelRatio(window.devicePixelRatio); // 设置设备像素比
  }
  private initRenderer() {
    this.viewerDom = document.getElementById(this.id) as HTMLElement
    this.renderer = new WebGLRenderer({
      logarithmicDepthBuffer: true,// 是否使用对数深度缓存。如果要在单个场景中处理巨大的比例差异，就有必要使用
      antialias: true, // true/false表示是否开启反锯齿
      alpha: true, // true/false 表示是否可以设置背景色透明
      precision: 'mediump', // highp/mediump/lowp 表示着色精度选择
      premultipliedAlpha: true, // true/false 表示是否可以设置像素深度（用来度量图像的分辨率）
      // preserveDrawingBuffer: false, // true/false 表示是否保存绘图缓冲
      // physicallyCorrectLights: true, // true/false 表示是否开启物理光照
    })
    //清除深度缓存。相当于调用.clear( false, true, false )
    this.renderer.clearDepth();

    //如果设置, 请在场景中使用阴影贴图。 默认是 false
    this.renderer.shadowMap.enabled = true;
    this.renderer.outputColorSpace = SRGBColorSpace; // 可以看到更亮的材质，同时这也影响到环境贴图。
    this.viewerDom.appendChild(this.renderer.domElement);
  }
    /**自定义鼠标事件触发的范围，给定一个模型组，对给定的模型组鼠标事件才生效 */
    public setRaycasterObjects (objList: THREE.Object3D[]): void {
      this.raycasterObjects = objList;
    }
  private getRaycasterIntersectObjects(): THREE.Intersection[] {
    if (!this.raycasterObjects.length) return [];
    // .setFromCamera()计算射线投射器`Raycaster`的射线属性.ray
    // 形象点说就是在点击位置创建一条射线，射线穿过的模型代表选中
    this.raycaster.setFromCamera(this.mouse, this.camera);
    // intersectObjects([mesh1, mesh2, mesh3])对参数中的网格模型对象进行射线交叉计算
    return this.raycaster.intersectObjects(this.raycasterObjects, true);
  }
}